#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from bufferedreader import *
import os, array


__whitespace = {
    0x0000, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x0020, 0x2002,
    0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A,
    0x2028, 0x2029, 0x202f, 0x205f, 0x3000, 0x00ff, 0xfffe, 0xffff,
}
__escape = {
    0x22: '\"', 0x27: '\'', 0x5c: '\\', 0x61: '\a', 0x62: '\b',
    0x65: '\033', 0x66: '\f', 0x6e: '\n', 0x72: '\r', 0x74: '\t',
    0x76: '\v', 0x41: '\a', 0x42: '\b', 0x45: '\033', 0x46: '\f',
    0x4e: '\n', 0x52: '\r', 0x54: '\t', 0x56: '\v',
}
__descape = {
    '\"': "\\\"", '\\\\': '\\\\', '\a': '\\a', '\b': '\\b',
    '\033': '\\e', '\f': '\\f', '\n': '\\n', '\r': '\\r', '\t': '\\t',
    '\v': '\\v', '\0': '\\0'
}

class SRONError (Exception):
    def __init__(self, type, message):
        self.type = type
        self.message = message
        super().__init__(message)
    def __str__(self):
        return f"{self.type}: {self.message}"
    def __repr__(self):
        return f"< SRONError {repr(self.type)}: {repr(self.message)} >"

def skip_whitespace(in_stream):
    step = 0
    for c in in_stream:
        match step:
            case 0:
                if c == 0x23: step = 1
                elif c == 0x2f: step = 2
                elif c not in __whitespace:
                    in_stream.seek(os.SEEK_CUR, -1)
                    return
            case 1:
                if c == 0x0a: step = 0
            case 2:
                if c == 0x2f: step = 3
                elif c == 0x2a: step = 4
                else:
                    in_stream.seek(os.SEEK_CUR, -2)
                    return
            case 3:
                if c == 0x0a: step = 0
            case 4:
                if c == 0x2a: step = 5
            case 5:
                if c == 0x2f: step = 0
                elif c == 0x2a: step = 5
                else: step = 4
            case _:
                raise SRONError("InternalError", "Invalid state")
    if step == 4 or step == 5:
        raise SRONError("EOF", "Unterminated comment")
    elif step == 2:
        in_stream.seek(os.SEEK_CUR, -1)
def isname_start(c):
    return c == 0x5f or (c >= 0x41 and c <= 0x5a) or (c >= 0x61 and c <= 0x7a)
def isname(c):
    return c == 0x5f or (c >= 0x41 and c <= 0x5a) or (c >= 0x61 and c <= 0x7a) or (c >= 0x30 and c <= 0x39)
def escape_parser(in_stream):
    c = in_stream.read()
    if c in __escape: return __escape[c]
    elif c == 0x30:
        c = in_stream.read()
        if c >= 0x30 and c <= 0x37:
            d = in_stream.read()
            if d >= 0x30 and d <= 0x37:
                return chr(((c - 0x30) << 3) + (d - 0x30))
            else:
                in_stream.seek(os.SEEK_CUR, -2)
                return '\0'
        else:
            in_stream.seek(os.SEEK_CUR, -1)
            return '\0'
    elif c >= 0x31 and c <= 0x37:
        d = in_stream.read()
        if d >= 0x30 and d <= 0x37:
            e = in_stream.read()
            if e >= 0x30 and e <= 0x37:
                return chr(((c - 0x30) << 6) + ((d - 0x30) << 3) + (e - 0x30))
            else:
                in_stream.seek(os.SEEK_CUR, -2)
                return '\0'
        else:
            in_stream.seek(os.SEEK_CUR, -1)
            return '\0'
    elif c == 0x58 or c == 0x78:
        c = in_stream.read()
        if c == 0x7b:
            value = 0
            len = 0
            for c in in_stream:
                if c >= 0x30 and c <= 0x39: value = (value << 4) + c - 0x30
                elif c >= 0x41 and c <= 0x46: value = (value << 4) + c - 0x37
                elif c >= 0x61 and c <= 0x66: value = (value << 4) + c - 0x57
                elif c == 0x7d: return chr(value)
                len += 1
            raise SRONError("ParseError", "Unterminated escape sequence")
        elif (c >= 0x30 and c <= 0x37) or (c >= 0x41 and c <= 0x46) or (c >= 0x61 and c <= 0x66):
            value = 0
            d = c
            if d >= 0x30 and d <= 0x39: value = (value << 4) + c - 0x30
            elif d >= 0x41 and d <= 0x46: value = (value << 4) + c - 0x37
            elif d >= 0x61 and d <= 0x66: value = (value << 4) + c - 0x57
            else:
                in_stream.seek(os.SEEK_CUR, -1)
                return chr(value)
            d = in_stream.read()
            if d >= 0x30 and d <= 0x39: value = (value << 4) + c - 0x30
            elif d >= 0x41 and d <= 0x46: value = (value << 4) + c - 0x37
            elif d >= 0x61 and d <= 0x66: value = (value << 4) + c - 0x57
            else:
                in_stream.seek(os.SEEK_CUR, -1)
                return chr(value)
            d = in_stream.read()
            if d >= 0x30 and d <= 0x39: value = (value << 4) + c - 0x30
            elif d >= 0x41 and d <= 0x46: value = (value << 4) + c - 0x37
            elif d >= 0x61 and d <= 0x66: value = (value << 4) + c - 0x57
            else:
                in_stream.seek(os.SEEK_CUR, -1)
                return chr(value)
            d = in_stream.read()
            if d >= 0x30 and d <= 0x39: value = (value << 4) + c - 0x30
            elif d >= 0x41 and d <= 0x46: value = (value << 4) + c - 0x37
            elif d >= 0x61 and d <= 0x66: value = (value << 4) + c - 0x57
            elif d == 0x0d or d == 0x0a: return None
            else:
                in_stream.seek(os.SEEK_CUR, -1)
                return chr(value)
            return chr(value)
        else: return chr(c)
def bool_parser(in_stream):
    skip_whitespace(in_stream)
    c = in_stream.read()
    if c == 116:
        if in_stream.read() == 114:
            if in_stream.read() == 117:
                if in_stream.read() == 101: return True
    elif c == 102:
        if in_stream.read() == 97:
            if in_stream.read() == 108:
                if in_stream.read() == 115:
                    if in_stream.read() == 101: return False
    raise SRONError("ParseError", "Expected true or false")
def null_parser(in_stream):
    skip_whitespace(in_stream)
    if in_stream.read() == 110:
        c = in_stream.read()
        if c == 117:
            if in_stream.read() == 108:
                if in_stream.read() == 108: return None
        elif c == 105:
            if in_stream.read() == 108: return None
    raise SRONError("ParseError", "Expected null or nil")
def dec_parser(in_stream):
    value = 0
    c = in_stream.read()
    if c >= 0x30 and c <= 0x39: value = c - 0x30
    else: raise SRONError("ParseError", "Expected integer")
    for c in in_stream:
        if c >= 0x30 and c <= 0x39: value = value * 10 + c - 0x30
        else:
            in_stream.seek(os.SEEK_CUR, -1)
            return value
    return value
def hex_parser(in_stream):
    value = 0
    c = in_stream.read()
    if c >= 0x30 and c <= 0x39: value = (value << 4) + c - 0x30
    elif c >= 0x41 and c <= 0x46: value = (value << 4) + c - 0x37
    elif c >= 0x61 and c <= 0x66: value = (value << 4) + c - 0x57
    else: raise SRONError("ParseError", "Expected integer")
    for c in in_stream:
        if c >= 0x30 and c <= 0x39: value = (value << 4) + c - 0x30
        elif c >= 0x41 and c <= 0x46: value = (value << 4) + c - 0x37
        elif c >= 0x61 and c <= 0x66: value = (value << 4) + c - 0x57
        else:
            in_stream.seek(os.SEEK_CUR, -1)
            return value
    return value
def bin_parser(in_stream):
    value = 0
    c = in_stream.read()
    if c == 0x30: value = 0
    elif c == 0x31: value = 1
    else: raise SRONError("ParseError", "Expected integer")
    for c in in_stream:
        if c == 0x30: value = (value << 1) + 0
        elif c == 0x31: value = (value << 1) + 1
        else:
            in_stream.seek(os.SEEK_CUR, -1)
            return value
    return value
def oct_parser(in_stream):
    value = 0
    c = in_stream.read()
    if c >= 0x30 and c <= 0x37: value = c - 0x30
    else: raise SRONError("ParseError", "Expected integer")
    for c in in_stream:
        if c >= 0x30 and c <= 0x37: value = (value << 3) + c - 0x30
        else:
            in_stream.seek(os.SEEK_CUR, -1)
            return value
    return value
def int_parser(in_stream):
    skip_whitespace(in_stream)
    c = in_stream.read()
    if c == 0x2d: return - dec_parser(in_stream)
    elif c == 0x2b: return + dec_parser(in_stream)
    elif c == 0x30:
        c = in_stream.read()
        if c == 120 or c == 88: return hex_parser(in_stream)
        elif c == 66 or c == 98: return bin_parser(in_stream)
        elif c >= 0x30 and c <= 0x37:
            in_stream.seek(os.SEEK_CUR, -1)
            return oct_parser(in_stream)
        else:
            in_stream.seek(os.SEEK_CUR, -1)
            return 0
    elif c >= 0x31 and c <= 0x39:
        in_stream.seek(os.SEEK_CUR, -1)
        return dec_parser(in_stream)
    raise SRONError("ParseError", "Expected integer")
def float_parser(in_stream):
    skip_whitespace(in_stream)
    c = in_stream.read()
    if c == 0x2d: return - float_parser(in_stream)
    elif c == 0x2b: return + float_parser(in_stream)
    elif c >= 0x30 and c <= 0x39:
        in_stream.seek(os.SEEK_CUR, -1)
        value = c - 0x30
        over = 0
        for c in in_stream:
            if c >= 0x30 and c <= 0x39: value = value * 10 + c - 0x30
            elif c == 0x2e:
                for c in in_stream:
                    if c >= 0x30 and c <= 0x39:
                        value = value * 10 + c - 0x30
                        over += 1
                    else:
                        in_stream.seek(os.SEEK_CUR, -1)
                        break
                return value / 10 ** over
            else: break
    elif c == 0x2e:
        value = 0
        over = 0
        for c in in_stream:
            if c >= 0x30 and c <= 0x39:
                value = value * 10 + c - 0x30
                over += 1
            else:
                in_stream.seek(os.SEEK_CUR, -1)
                break
        return value / 10 ** over
    raise SRONError("ParseError", "Expected float")
def string_parser(in_stream):
    skip_whitespace(in_stream)
    res = []
    c = in_stream.read()
    if c == 0x22:
        d = in_stream.read()
        if d == 0x22:
            e = in_stream.read()
            if e == 0x22:
                step = 0
                for c in in_stream:
                    match step:
                        case 0:
                            if c == 0x22: step = 1
                            elif c == 0x5c: res.append(escape_parser(in_stream))
                            else: res.append(escape_parser(in_stream) if c == 0x5c else chr(c))
                        case 1:
                            if c == 0x22: step = 2
                            else:
                                res.append(0x22)
                                res.append(escape_parser(in_stream) if c == 0x5c else chr(c))
                                step = 0
                        case 2:
                            if c == 0x22: return "".join(res)
                            else:
                                res.append(0x22); res.append(0x22)
                                res.append(escape_parser(in_stream) if c == 0x5c else chr(c))
                                step = 0
                raise SRONError("ParseError", "Unterminated string")
            else:
                in_stream.seek(os.SEEK_CUR, -1)
                return "\"" + string_parser(in_stream) + "\""
        else:
            in_stream.seek(os.SEEK_CUR, -1)
            for c in in_stream:
                if c == 0x22: return "".join(res)
                else: res.append(escape_parser(in_stream) if c == 0x5c else chr(c))
            raise SRONError("ParseError", "Unterminated string")
    else: raise SRONError("ParseError", "Expected string")
def array_parser(in_stream, _parser):
    skip_whitespace(in_stream)
    c = in_stream.read()
    if c == 0x5b:
        res: list[object] = []
        skip_whitespace(in_stream)
        for c in in_stream:
            if c == 0x5d: return res
            elif c == 0x2c: continue
            else:
                in_stream.seek(os.SEEK_CUR, -1)
                res.append(array_item_parser(in_stream, _parser))
            skip_whitespace(in_stream)
        raise SRONError("ParseError", "Unterminated array")
    else: raise SRONError("ParseError", "Expected array")
def array_item_parser(in_stream, _parser):
    pos = in_stream.tell()
    for guess in _parser:
        func = _parser[guess][0]
        tabled = _parser[guess][1]
        try: return (guess, func(in_stream, _parser) if tabled else func(in_stream))
        except SRONError: in_stream.seek(os.SEEK_SET, pos)
    raise SRONError("TypeError", "Unknown type")

def dict_constructor(data, path, _constructor):
    res = []
    path += '.'
    for key in data:
        typeof, value = data[key]
        if typeof == "dict":
            res.append(dict_constructor(value, path + key, _constructor))
        elif typeof in _constructor:
            func = _constructor[typeof][0]
            tabled = _constructor[typeof][1]
            res.append(f"{path + key}: {typeof} = {func(value, _constructor) if tabled else func(value)}\n")
        else: raise SRONError("TypeError", "Unknown type")
    return "".join(res)
def array_constructor(data, _constructor):
    res = ['[ ']
    for i in range(len(data)):
        typeof, value = data[i]
        if typeof in _constructor:
            func = _constructor[typeof][0]
            tabled = _constructor[typeof][1]
            res.append(f"{func(value, _constructor) if tabled else func(value)}")
            res.append(', ')
        else: raise SRONError("TypeError", "Unknown type")
    res.pop()
    res.append(']')
    return "".join(res)
def float_constructor(data): return str(data)
def int_constructor(data): return str(data)
def string_constructor(data):
    res = ['"']
    cnt = 0
    for c in data:
        if c in __descape:
            if c == "\n": cnt += 1
            res.append(__descape[c])
        else: res.append(c)
    if cnt > 3:
        res[0] = '"""'
        for i in range(len(res)):
            if res[i] == '\\n': res[i] = '\n'
        res.append('""')
    res.append('"')
    return "".join(res)
def bool_constructor(data): return "true" if data else "false"
def null_constructor(data): return "null"


class SRON:
    def __init__(self):
        self._parser = {
            "float": (float_parser, False),
            "int": (int_parser, False),
            "bool": (bool_parser, False),
            "null": (null_parser, False),
            "string": (string_parser, False),
            "array": (array_parser, True),
        }
        self._constructor = {
            "float": (float_constructor, False),
            "int": (int_constructor, False),
            "bool": (bool_constructor, False),
            "null": (null_constructor, False),
            "string": (string_constructor, False),
            "array": (array_constructor, True),
            "dict": (dict_constructor, True),
        }
    @staticmethod
    def __insert (d, path, typeof, value):
        if len(path) == 1:
            d[path[0]] = (typeof, value)
        else:
            if path[0] not in d: d[path[0]] = ("dict", {})
            __class__.__insert(d[path[0]][1], path[1:], typeof, value)
    def parse_path(self, in_stream):
        path = []
        name = []
        skip_whitespace(in_stream)
        for c in in_stream:
            if c == 0x2e:
                path.append("".join(array.array('I', name).tobytes().decode('utf-32-le')))
                name.clear()
            elif c == 0x3a or c == 0x3d:
                path.append("".join(array.array('I', name).tobytes().decode('utf-32-le')))
                return (c, path)
            elif isname_start(c):
                if name:
                    raise SRONError("PathError", "Unexpected name start")
                name.append(c)
                for c in in_stream:
                    if isname(c): name.append(c)
                    else:
                        in_stream.seek(os.SEEK_CUR, -1)
                        break
            else:
                print(in_stream.tell())
                raise SRONError("PathError", f"Unexpected character {hex(c)} : {chr(c)}")
            skip_whitespace(in_stream)
        if name or path:
            raise SRONError("EOF", "Unterminated path")
        raise SRONError("FriendlyEOF", "EOF")
    def parse_type(self, in_stream):
        name = []
        skip_whitespace(in_stream)
        c = in_stream.read()
        if isname_start(c):
            name.append(c)
            for c in in_stream:
                if isname(c): name.append(c)
                else: break
            in_stream.seek(os.SEEK_CUR, -1)
            skip_whitespace(in_stream)
            if in_stream.read() == 0x3d:
                return "".join(array.array('I', name).tobytes().decode('utf-32-le'))
            raise SRONError("TypeError", f"Unexpected character {hex(c)} : {chr(c)}")
        raise SRONError("TypeError", f"Unexpected character {hex(c)} : {chr(c)}")
    def parse_line (self, in_stream):
        path = self.parse_path(in_stream)
        if path[0] == 0x3a:
            typeof = self.parse_type(in_stream)
        else: typeof = "auto"
        typeof, value = self.parse_value(in_stream, typeof)
        return (path[1], typeof, value)
    def parse_value (self, in_stream, typeof):
        if typeof == "auto":
            pos = in_stream.tell()
            for guess in self._parser:
                func = self._parser[guess][0]
                tabled = self._parser[guess][1]
                try: return (guess, func(in_stream, self._parser) if tabled else func(in_stream))
                except SRONError: in_stream.seek(os.SEEK_SET, pos)
            raise SRONError("TypeError", "Unknown type")
        elif typeof in self._parser:
            func = self._parser[typeof][0]
            tabled = self._parser[typeof][1]
            return (typeof, func(in_stream, self._parser) if tabled else func(in_stream))
        else: raise SRONError("TypeError", f"Unknown type {typeof}")
    def parse(self, in_stream):
        res = {}
        while True:
            try:
                path, typeof, value = self.parse_line(in_stream)
                __class__.__insert(res, path, typeof, value)
            except SRONError as e:
                if e.type == "FriendlyEOF": break
                else: raise e
        return res
    def construct(self, data):
        res = []
        for key in data:
            typeof, value = data[key]
            if typeof == "dict":
                res.append(dict_constructor(value, key, self._constructor))
            elif typeof in self._constructor:
                func = self._constructor[typeof][0]
                tabled = self._constructor[typeof][1]
                res.append(f"{key}: {typeof} = {func(value, self._constructor) if tabled else func(value)}\n")
            else: raise SRONError("TypeError", f"Unknown type {typeof}")
        return "".join(res)
    def __call__(self, in_stream):
        if isinstance(in_stream, str) or isinstance(in_stream, bytes) or isinstance(in_stream, bytearray):
            return self.parse(UnicodeReadBuffer(in_stream))
        elif isinstance(in_stream, ReadBuffer): return self.parse(in_stream)
        elif isinstance(in_stream, dict): return self.construct(in_stream)
        else: raise SRONError("TypeError", "Unknown type")
    def pop_parser(self, typeof):
        if typeof in self._parser: self._parser.pop(typeof)
        else: raise SRONError("TypeError", f"Unknown type {typeof}")
    def pop_constructor(self, typeof):
        if typeof in self._constructor: self._constructor.pop(typeof)
        else: raise SRONError("TypeError", f"Unknown type {typeof}")
    def push_parser(self, typeof, func, tabled):
        self._parser[typeof] = (func, tabled)
    def push_constructor(self, typeof, func, tabled):
        self._constructor[typeof] = (func, tabled)

if __name__ == "__main__":
    origin = """
# This is a comment
/* This is a multiline comment */
// This is another comment
bool = true
null = null
int.dec = 123
int.bin = 0b101
int.hex = 0x114
int.oct = 0514
int.signed = -19198
float.unsigned = 123.456
float.signed = -123.456
string.single = "Hello, world!"
string.triple = \"\"\"
    Hello, world!\"\"\"
string.escaped = "Hello, world!\\n\\t\\r\\b\\f\\a\\v\\0\\x22\\x27\\x5c"
array = [1, .14, "514", true, [9, 1, 9, 8, 1], null]
"""
    a = SRON()
    print("Original:")
    print(origin)

    parsed = a(origin)
    print("Parsed:")
    print(parsed)

    constructed = a.construct(parsed)
    print("Constructed:")
    print(constructed)

    print("Reparsed:")
    print(a(constructed))
